package com.github.icloudpay.pay.core.service.refund.wechat.service;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.net.ssl.SSLContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.lang.ObjectUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.github.icloudpay.pay.core.feign.PayChannelConfigFeign;
import com.github.icloudpay.pay.core.inter.RefundService;
import com.github.icloudpay.pay.core.service.pay.wechat.service.WechatSignatureService;
import com.github.icloudpay.pay.core.service.pay.wechat.util.WechatPayConfig;
import com.github.wxiaoqi.security.common.admin.pay.request.RefundRequest;
import com.github.wxiaoqi.security.common.admin.pay.request.WechatRefundRequest;
import com.github.wxiaoqi.security.common.admin.pay.response.RefundResponse;
import com.github.wxiaoqi.security.common.admin.pay.response.WechatRefundResponse;
import com.github.wxiaoqi.security.common.exception.BusinessException;
import com.github.wxiaoqi.security.common.msg.ResponseCode;
import com.github.wxiaoqi.security.common.util.Utils;

/**
 * 微信退款
 * @author hexufeng
 */
@SuppressWarnings("deprecation")
@Service("wechatRefundService")
public class WechatRefundService extends RefundService {
    
    private static final Logger logger = (Logger) LoggerFactory.getLogger(WechatRefundService.class);
    
    @Autowired
    private PayChannelConfigFeign payChannelConfigFeign;
    @Autowired
    private WechatSignatureService wechatSignatureService;
    @Autowired
    private WechatPayConfig wechatPayConfig;

    @Override
    public RefundResponse refundNoPwd(RefundRequest request) {
    	
    	logger.info("****************调用微信退款接口开始*******************");
    	
    	RefundResponse refundResponse = new RefundResponse();

    	Map<String, Object> reqMap = new HashMap<String, Object>();
    	reqMap.put("platformId", request.getPlatformId());
    	reqMap.put("merchantId", request.getMerchantId());
    	reqMap.put("payChannelNo", request.getPayChannelNo());
		Map<String, Object> respMap = payChannelConfigFeign.query(reqMap);
        if(!(boolean) respMap.get("success")){
			refundResponse.setSuccess(false);
			refundResponse.setCode(ResponseCode.PAYCHANNEL_CONFIGURATION_NOTEXIST.getCode());
			refundResponse.setMsg(ResponseCode.PAYCHANNEL_CONFIGURATION_NOTEXIST.getMessage());
			return refundResponse;
		}
    	
        @SuppressWarnings("unchecked")
		Map<String, Object> configMap = (Map<String, Object>)respMap.get("payChannelConfigInfo");
        
        logger.info("支付通道配置信息：{}", configMap);
        
        WechatRefundRequest wechatRefundRequest = new WechatRefundRequest();
        wechatRefundRequest.setAppid((String)configMap.get("outMerAccount"));
        wechatRefundRequest.setMch_id((String)configMap.get("outMerNo"));
        wechatRefundRequest.setNonce_str(ObjectUtils.toString((new Random().nextInt() * (99999 - 10000 + 1)) + 10000));
        wechatRefundRequest.setOp_user_id((String)configMap.get("outMerNo"));
        wechatRefundRequest.setOut_trade_no(request.getSerialNo());
        wechatRefundRequest.setOut_refund_no(request.getRefundSerialNo());
        wechatRefundRequest.setTotal_fee(Utils.changeToDivide(request.getOrdAmt().toString()));
        wechatRefundRequest.setRefund_fee(Utils.changeToDivide(request.getRefundAmt().toString()));
        try {
			wechatRefundRequest.setSign(wechatSignatureService.sign(wechatRefundRequest, configMap));
		} catch (Exception e1) {
			e1.printStackTrace();
		}
        
        //调用微信退款接口
        HttpClient httpClient = HttpClientBuilder.create().setSSLSocketFactory(loadMerKey((String)configMap.get("keyPath"),(String)configMap.get("keyPwd"))).build();
        HttpPost post = new HttpPost(wechatPayConfig.getWechatGatewayRefundUrl());
        try {
            String xmlStr = reqData2Xml(wechatRefundRequest);
            post.setEntity(new StringEntity(xmlStr));
            WechatRefundResponse wechatRefundResponse = xmlToResponseData(httpClient.execute(post).getEntity().getContent());
            
            logger.info("微信退款返回:{}",wechatRefundResponse);
            
            if("SUCCESS".equals(wechatRefundResponse.getResultCode())){
                //退款成功
                refundResponse.setStatus("02");//退款处理中
            }else{
                //退款失败
                refundResponse.setStatus("01");//退款失败
                refundResponse.setErrorMsg(wechatRefundResponse.getErrCodeDes());
                if(!StringUtils.hasText(refundResponse.getErrorMsg())){
                    refundResponse.setErrorMsg(wechatRefundResponse.getReturnMsg());
                }
            }
            refundResponse.setSuccess(true);
            refundResponse.setCode(ResponseCode.OK.getCode());
            refundResponse.setMsg(ResponseCode.OK.getMessage());
            return refundResponse;
        } catch (ClientProtocolException e) {
            logger.error("调用微信退款失败",e);
            refundResponse.setSuccess(false);
            refundResponse.setCode(ResponseCode.WECHAT_REFUND_FAIL.getCode());
            refundResponse.setMsg(ResponseCode.WECHAT_REFUND_FAIL.getMessage());
			return refundResponse;
        } catch (IOException e) {
            if(e instanceof SocketTimeoutException){
                //超时
                logger.error("调用微信退款超时",e);
                refundResponse.setSuccess(false);
                refundResponse.setCode(ResponseCode.WECHAT_REFUND_TIME_OUT.getCode());
                refundResponse.setMsg(ResponseCode.WECHAT_REFUND_TIME_OUT.getMessage());
                refundResponse.setStatus("03");//超时
    			return refundResponse;
            }
            logger.error("调用微信退款失败",e);
            refundResponse.setSuccess(false);
            refundResponse.setCode(ResponseCode.WECHAT_REFUND_FAIL.getCode());
            refundResponse.setMsg(ResponseCode.WECHAT_REFUND_FAIL.getMessage());
			return refundResponse;
        }catch (JAXBException e) {
            logger.error("解析微信数据失败",e);
            refundResponse.setSuccess(false);
            refundResponse.setCode(ResponseCode.WECHAT_REFUND_FAIL.getCode());
            refundResponse.setMsg(ResponseCode.WECHAT_REFUND_FAIL.getMessage());
			return refundResponse;
        }
        
    }
    
    /**
     * 将请球参数转为Xml格式
     * @throws JAXBException 
     * @throws ParserConfigurationException 
     */
    private String reqData2Xml(WechatRefundRequest wechatRefundRequest) throws JAXBException{
        JAXBContext context = JAXBContext.newInstance(WechatRefundRequest.class);
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_ENCODING,"UTF-8");//编码格式
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);//是否格式化生成的xml串
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);//是否省略xml头信息（<?xml version="1.0" encoding="gb2312" standalone="yes"?>）
        
        ByteArrayOutputStream outputstream = new ByteArrayOutputStream();
        StreamResult result = new StreamResult(outputstream);
        marshaller.marshal(wechatRefundRequest, result);
        byte[] body = outputstream.toByteArray();
        
        return new String(body,Charset.forName("UTF-8"));
    }
    
    private WechatRefundResponse xmlToResponseData(InputStream inputStream) throws JAXBException{
        Reader reader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
        JAXBContext context = JAXBContext.newInstance(WechatRefundResponse.class);
        try{
            Unmarshaller unmarshaller=  context.createUnmarshaller();
            return (WechatRefundResponse) unmarshaller.unmarshal(reader);
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    private SSLConnectionSocketFactory loadMerKey(String keyFilePath,String keyPwd){
        if(keyFilePath == null){
            logger.error("商户退款密钥证书未配置keyFilePath为空");
            throw new BusinessException("商户退款密钥证书未配置");
        }
        FileInputStream instream;
        try {
            instream = new FileInputStream(new File(keyFilePath));
        } catch (FileNotFoundException e) {
            logger.error("商户退款密钥证书未配置",e);
            throw new BusinessException("商户退款密钥证书未配置");
        }
        try {
            KeyStore keyStore  = KeyStore.getInstance("PKCS12");
            keyStore.load(instream, keyPwd.toCharArray());
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, keyPwd.toCharArray())
                    .build();
            return new SSLConnectionSocketFactory(
                    sslcontext,
                    new String[] { "TLSv1" },
                    null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        }catch (Exception e) {
            logger.error("加载商户密钥证书失败",e);
            throw new BusinessException("加载商户密钥证书失败");
        } finally {
            try {
                instream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
